
package com.jf.cloud.multishop.service.impl;

import com.jf.cloud.common.cache.constant.CacheNames;
import com.jf.cloud.common.cache.constant.LockCacheNames;
import com.jf.cloud.common.cache.constant.MultishopCacheNames;
import com.jf.cloud.common.cache.util.RedisUtil;
import com.jf.cloud.multishop.constant.ShopWalletZoneConstant;
import com.jf.cloud.multishop.mapper.ShopWalletMapper;
import com.jf.cloud.multishop.mapper.ShopWalletZoneMapper;
import com.jf.cloud.multishop.model.ShopWalletZone;
import com.jf.cloud.multishop.service.ShopWalletZoneService;
import com.jf.cloud.multishop.vo.ShopWalletVO;
import com.jf.cloud.multishop.vo.ShopWalletZoneInfoVO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author zz
 */
@Service
public class ShopWalletZoneServiceImpl implements ShopWalletZoneService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ShopWalletZoneServiceImpl.class);

    @Autowired
    private ShopWalletZoneMapper shopWalletZoneMapper;
    @Autowired
    private ShopWalletMapper shopWalletMapper;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void update(ShopWalletZone shopWalletZone) {
        shopWalletZoneMapper.update(shopWalletZone);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doZone( Long shopId,Integer paySysType) {
        RLock lock = redissonClient.getLock(LockCacheNames.LOCK_SHOP_WALLET_ZONE_PREFIX + shopId);
        lock.lock();
        try {
            // 分片前检查分片数量，避免并发导致重复分片
            ShopWalletVO shopWallet = shopWalletMapper.getByShopId(shopId);
            if (Objects.equals(shopWallet.getZoneNum(), ShopWalletZoneConstant.ZONE_NUM)) {
                return;
            }
            LOGGER.info("店铺钱包分片开始...{}", shopId);
            // 要分成的片数是 n + 1的模式
            // 假设有1000块钱，要分成101份，那么向上取整平均每份是100块，最后一个次分片是0
            Long mainSettleAmount = divToRoundUp(shopWallet.getSettledAmount(), ShopWalletZoneConstant.ZONE_NUM + 1);

            List<ShopWalletZone> shopWalletZones = getShopWalletZones(shopId, ShopWalletZoneConstant.ZONE_NUM, mainSettleAmount, shopWallet.getSettledAmount());

            // 插入各个分片
            shopWalletZoneMapper.batchSave(shopWalletZones);

            // 更新主分片的结算金额和分片状态
            shopWalletMapper.updateZoneNum(ShopWalletZoneConstant.ZONE_NUM, shopId, mainSettleAmount);
        } finally {
            lock.unlock();
        }
    }

    @Override
    @Cacheable(cacheNames = MultishopCacheNames.SHOP_WALLET_ZONE_INFO_KEY, key = "#shopId", sync = true)
    public ShopWalletZoneInfoVO getShopZoneInfo(Long shopId) {
        return shopWalletMapper.getShopWalletZoneInfo(shopId);
    }

    @Override
    public void removeZoneCache(List<Long> shopIds) {
        List<String> keyList = new ArrayList<>();
        for (Long shopId : shopIds) {
            keyList.add(MultishopCacheNames.SHOP_WALLET_ZONE_INFO_KEY + CacheNames.UNION + shopId);
        }
        RedisUtil.deleteBatch(keyList);
    }

    @Override
    public void updateZoneSettleAmount(Long shopId, Long totalSettleAmount, int zoneNum) {

        Long mainSettleAmount = divToRoundUp(totalSettleAmount, zoneNum + 1);
        // 更新主分片的结算金额和分片状态
        shopWalletMapper.updateZoneNum(zoneNum, shopId, mainSettleAmount);

        List<ShopWalletZone> shopWalletZoneList = new ArrayList<>();
        // 分成对应片数
        for (int i = 0; i < zoneNum; i++) {
            ShopWalletZone shopWalletZone = new ShopWalletZone();
            shopWalletZone.setShopId(shopId);
            shopWalletZone.setZoneNo(i);
            if (totalSettleAmount - mainSettleAmount >= 0) {
                shopWalletZone.setSettledAmount(mainSettleAmount);
                // 减少剩余结算金额
                totalSettleAmount -= mainSettleAmount;
            } else {
                // 把剩下的金额全部给该分片，往后的分片都是0
                shopWalletZone.setSettledAmount(totalSettleAmount);
                totalSettleAmount = 0L;
            }
            shopWalletZoneList.add(shopWalletZone);
        }

        // 更新店铺分片的结算金额
        shopWalletZoneMapper.batchUpdate(shopWalletZoneList);
    }

    private List<ShopWalletZone> getShopWalletZones(Long shopId, int zoneNum, Long mainSettleAmount, Long totalSettleAmount) {
        List<ShopWalletZone> shopWalletZoneList = new ArrayList<>();
        totalSettleAmount -= mainSettleAmount;
        // 分成对应片数
        for (int i = 0; i < zoneNum; i++) {
            ShopWalletZone shopWalletZone = new ShopWalletZone();
            shopWalletZone.setShopId(shopId);
            shopWalletZone.setZoneNo(i);
            shopWalletZone.setUnsettledAmount(0L);
            if (totalSettleAmount - mainSettleAmount >= 0) {
                shopWalletZone.setSettledAmount(mainSettleAmount);
                // 减少剩余结算金额
                totalSettleAmount -= mainSettleAmount;
            } else {
                // 把剩下的金额全部给该分片，往后的分片都是0
                shopWalletZone.setSettledAmount(totalSettleAmount);
                totalSettleAmount = 0L;
            }
            shopWalletZone.setTotalSettledAmount(0L);
            shopWalletZoneList.add(shopWalletZone);
        }
        return shopWalletZoneList;
    }

    /**
     * 两个数相除然后向上取整
     * @param a 除数
     * @param b 被除数
     * @return 返回值
     */
    private long divToRoundUp(long a, int b) {
        return new BigDecimal(a).divide(new BigDecimal(b),0, RoundingMode.UP).longValue();
    }
}
